package top.yueyazhui.ask_for_leave.service.impl;

import cn.hutool.core.util.ObjectUtil;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.yueyazhui.ask_for_leave.dto.AskForLeaveDto;
import top.yueyazhui.ask_for_leave.dto.TaskDto;
import top.yueyazhui.ask_for_leave.entity.User;
import top.yueyazhui.ask_for_leave.mapper.UserMapper;
import top.yueyazhui.ask_for_leave.model.Response;
import top.yueyazhui.ask_for_leave.service.AskForLeaveService;
import top.yueyazhui.ask_for_leave.service.UserService;
import top.yueyazhui.ask_for_leave.util.UserUtil;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @Author yueyazhui
 * @Date 2023/2/11
 */
@Service
public class AskForLeaveServiceImpl implements AskForLeaveService {

    @Autowired
    RuntimeService runtimeService;
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    TaskService taskService;
    @Autowired
    HistoryService historyService;
    @Autowired
    ProcessEngine processEngine;
    @Autowired
    UserService userService;

    @Override
    public Response apply(AskForLeaveDto askForLeaveDto) {
        try {
            String currentUserId = UserUtil.getCurrentUser().getId().toString();
            // 设置流程发起人（默认 UserUtil.getCurrentUser().getUserName()）
            Authentication.setAuthenticatedUserId(currentUserId);
            Map<String, Object> vars = new HashMap<>();
            vars.put("applicantId", currentUserId);
            vars.put("days", askForLeaveDto.getDays());
            vars.put("startDate", askForLeaveDto.getStartDate());
            vars.put("endDate", askForLeaveDto.getEndDate());
            vars.put("reason", askForLeaveDto.getReason());
            vars.put("approveUserId", askForLeaveDto.getApproveUserId());
            runtimeService.startProcessInstanceByKey("AskForLeave", vars);
            return Response.success("请假申请提交成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Response.error("请假申请提交失败");
        }
    }

    @Override
    public List<User> getApproveUserList() {
        return userService.getApproveUserList(UserUtil.getCurrentUser().getId());
    }

    /**
     * 当前用户待审批流程列表
     * @return
     */
    @Override
    public List<AskForLeaveDto> getPendApproveList() {
        List<AskForLeaveDto> askForLeaveDtoList = new ArrayList<>();
        List<Execution> executionList = runtimeService.createExecutionQuery().startedBy(UserUtil.getCurrentUser().getId().toString()).list();
        for (Execution execution : executionList) {
            Integer days = ((Integer) runtimeService.getVariable(execution.getId(), "days"));
            Date startDate = (Date) runtimeService.getVariable(execution.getId(), "startDate");
            Date endDate = (Date) runtimeService.getVariable(execution.getId(), "endDate");
            String reason = (String) runtimeService.getVariable(execution.getId(), "reason");
            String approveUserId = ((String) runtimeService.getVariable(execution.getId(), "approveUserId"));
            AskForLeaveDto askForLeaveDto = new AskForLeaveDto();
            askForLeaveDto.setDays(days);
            askForLeaveDto.setStartDate(startDate);
            askForLeaveDto.setEndDate(endDate);
            askForLeaveDto.setReason(reason);
            askForLeaveDto.setApproveUserId(approveUserId);
            askForLeaveDto.setApproveUserName(userService.getUsernameById(Integer.parseInt(approveUserId)));
            askForLeaveDto.setProcessInstanceId(execution.getProcessInstanceId());
            askForLeaveDtoList.add(askForLeaveDto);
        }
        return askForLeaveDtoList;
    }

    /**
     * 实时进度图
     * @return
     */
    @Override
    public void getRealTimeProgressImage(String processInstanceId) throws IOException {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("AskForLeave").latestVersion().singleResult();
        // 绘制流程图的对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        // 绘制流程图的生成器
        DefaultProcessDiagramGenerator defaultProcessDiagramGenerator = new DefaultProcessDiagramGenerator();
        // 所有已经执行过的活动 ID，将之保存在这个集合中
        List<String> highLightedActivities = new ArrayList<>();
        // 所有已经执行过的线条 ID，将之保存在这个集合中
        List<String> highLightedFlows  = new ArrayList<>();
        // 缩放因子
        double scaleFactor = 1.0;
        // 绘制连接线时，是否绘制对应的文本
        boolean drawSequenceFlowNameWithNoLabelDI = true;
        // 查询流程实例的所有活动信息
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            if (historicActivityInstance.getActivityType().equals("sequenceFlow")) {
                // 如果活动类型是 sequenceFlow，那么就加入到线条的 ID 中
                highLightedFlows.add(historicActivityInstance.getActivityId());
            } else {
                // 否则就加入到高亮活动的 ID 中
                highLightedActivities.add(historicActivityInstance.getActivityId());
            }
        }
        ProcessEngineConfiguration processEngineConfig = processEngine.getProcessEngineConfiguration();
        InputStream is = defaultProcessDiagramGenerator.generateDiagram(bpmnModel, "PNG", highLightedActivities, highLightedFlows, processEngineConfig.getActivityFontName(), processEngineConfig.getLabelFontName(), processEngineConfig.getAnnotationFontName(), processEngineConfig.getClassLoader(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
        assert ObjectUtil.isNotNull(response);
        response.setContentType(MediaType.IMAGE_PNG_VALUE);
        FileCopyUtils.copy(is, response.getOutputStream());
    }

    @Override
    public List<TaskDto> getTaskList() {
        List<TaskDto> taskDtoList = new ArrayList<>();
        List<Task> taskList = taskService.createTaskQuery().taskAssignee(UserUtil.getCurrentUser().getId().toString()).list();
        for (Task task : taskList) {
            TaskDto taskDto = new TaskDto();
            Map<String, Object> variables = taskService.getVariables(task.getId());
            taskDto.setApplicantId(((String) variables.get("applicantId")));
            taskDto.setApplicantName(userService.getUsernameById(Integer.parseInt(variables.get("applicantId").toString())));
            taskDto.setDays(((Integer) variables.get("days")));
            taskDto.setStartDate(((Date) variables.get("startDate")));
            taskDto.setEndDate(((Date) variables.get("endDate")));
            taskDto.setReason(((String) variables.get("reason")));
            taskDto.setApproveUserId(((String) variables.get("approveUserId")));
            taskDto.setApproveUserName(userService.getUsernameById(Integer.parseInt(variables.get("approveUserId").toString())));
            taskDto.setTaskId(task.getId());
            taskDtoList.add(taskDto);
        }
        return taskDtoList;
    }

    @Override
    public Response approve(TaskDto taskDto) {
        try {
            Map<String, Object> vars = new HashMap<>();
            vars.put("approve", taskDto.getApprove());
            taskService.complete(taskDto.getTaskId(), vars);
            return Response.success("审批成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Response.success("审批失败");
        }
    }

    @Override
    public List<AskForLeaveDto> getHistoryList() {
        List<AskForLeaveDto> askForLeaveDtoList = new ArrayList<>();
        List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().startedBy(UserUtil.getCurrentUser().getId().toString()).finished().list();
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstanceList) {
            AskForLeaveDto askForLeaveDto = new AskForLeaveDto();
            List<HistoricVariableInstance> historicVariableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId()).list();
            for (HistoricVariableInstance historicVariableInstance : historicVariableInstanceList) {
                String variableName = historicVariableInstance.getVariableName();
                Object value = historicVariableInstance.getValue();
                switch (variableName) {
                    case "days":
                        askForLeaveDto.setDays(((Integer) value));
                        break;
                    case "startDate":
                        askForLeaveDto.setStartDate(((Date) value));
                        break;
                    case "endDate":
                        askForLeaveDto.setEndDate(((Date) value));
                        break;
                    case "reason":
                        askForLeaveDto.setReason(((String) value));
                        break;
                    case "approveUserId":
                        askForLeaveDto.setApproveUserId(((String) value));
                        askForLeaveDto.setApproveUserName(userService.getUsernameById(Integer.parseInt(askForLeaveDto.getApproveUserId())));
                        break;
                    case "approve":
                        askForLeaveDto.setApprove(((Boolean) value));
                        break;
                }
            }
            askForLeaveDto.setProcessInstanceId(historicProcessInstance.getId());
            askForLeaveDtoList.add(askForLeaveDto);
        }
        return askForLeaveDtoList;
    }
}
